import os.path
from PyQt5.QtWidgets import QPushButton

import py3func
from cui import Enum_Btn_Status
from parsermk.btnparser import DataBtnParser

LOG = py3func.make_logging(__file__)
Set_Btns_LightSticky = set()

class StickyButton(QPushButton):
    # 由于需要通过事件捕获button，故必须使用继承以便得到含有自定义信息的sender
    def __init__(self, parent_widget, str_btn_id, btns_parser: DataBtnParser, zoom_x, zoom_y):
        super().__init__(parent_widget)
        # @attribute
        self.objectName = str_btn_id
        self.zoom_x = zoom_x
        self.zoom_y = zoom_y
        # self.cmdExecutor = DataCmdParser()
        # self.sticky = False
        # self.downEx = Enum_Btn_Status["released"]
        # self.last_key_status = False

        self.sticky = btns_parser.sticky(self.objectName)
        if self.sticky:
            LOG.debug("Here is a sticky button[%s]" % self.objectName)
        self.downEx = Enum_Btn_Status["released"]

        self.set_facade(btns_parser)
        self.cmdExecutor = btns_parser.get_cmd_parser(self.objectName)

        # 连接信号槽：利用lambda轻松实现slot函数调用时传递附属参数
        self.pressed.connect(lambda: self.on_clicked(False))
        self.released.connect(lambda: self.on_clicked(True))

    def set_facade(self, btns_parser: DataBtnParser):  # just called when initializing
        # set the geometry
        xml_geometry = btns_parser.geometry(self.objectName)
        real_geometry = [   self.zoom_x * xml_geometry[0],
                            self.zoom_y * xml_geometry[1],
                            self.zoom_x * xml_geometry[2],
                            self.zoom_y * xml_geometry[3]]
        self.setGeometry(*real_geometry)

        # set button text
        self.setText(btns_parser.text(self.objectName))

        # 背景图初始化刷新
        self.prepare_showing()
        self.update_showing()

    def prepare_showing(self):
        pass

    def update_showing(self):
        pass

    def _check_image_path(self, str_btn_status):
        # 由于函数只在self.image存在时被调用，故省略self.image合法性判断
        file_name, file_ext = os.path.splitext(self.image)
        file_name_status = file_name + "_%d" % Enum_Btn_Status[str_btn_status]
        image_status = file_name_status + file_ext
        if not os.path.exists(image_status):
            # raise FileExistsError
            LOG.error("Fail to find the button's %s-image, replaced by released-image" % str_btn_status)
            return self.image  # 如果找不到状态图片，则调用该按钮的默认图片
        else:
            return image_status

    def _update_onSticky(self):
        """ last_key_status 用于保存此次按下之前的状态位 """
        global Set_Btns_LightSticky

        if not hasattr(self, "last_key_status") or self.last_key_status == Enum_Btn_Status["released"]:
            self.last_key_status = Enum_Btn_Status["light_sticky"]
            Set_Btns_LightSticky.add(self)
        elif self.last_key_status == Enum_Btn_Status["light_sticky"]:
            self.last_key_status = Enum_Btn_Status["sticky"]
            Set_Btns_LightSticky.remove(self)
        elif self.last_key_status == Enum_Btn_Status["sticky"]:
            self.last_key_status = Enum_Btn_Status["released"]
        self.downEx = self.last_key_status

    def _update_down_status(self, isReleased):
        """ 更新 downEx 属性 """
        if not isReleased:
            self.downEx = Enum_Btn_Status["pressed"]
        elif not self.sticky:
            self.downEx = Enum_Btn_Status["released"]
        else:
            self._update_onSticky()
        LOG.debug("The button's down-status update to [%d]" % self.downEx)

    def just_one_time_sticky(self):
        """ 清除 light-sticky 状态位 """
        global Set_Btns_LightSticky

        if Set_Btns_LightSticky:
            for btn in Set_Btns_LightSticky:
                LOG.debug("Clear the LightWeight sticky Button[%s]" % btn.objectName)

                btn.downEx = Enum_Btn_Status["released"]
                btn.last_key_status = Enum_Btn_Status["released"]
                btn.update_showing()
                btn.cmdExecutor.execute_command(btn.downEx)
            Set_Btns_LightSticky.clear()

    def on_clicked(self, isReleased: bool):
        """ slot when the button is clicked """
        self._update_down_status(isReleased)  # 更新 downEx 属性
        self.update_showing()  # 更新 image 背景图
        self.cmdExecutor.execute_command(self.downEx)   # 调用功能

        # 释放一个非sticky的按键，在功能执行完成后清理light-sticky
        if isReleased and not self.sticky:
            self.just_one_time_sticky()


class ImageButton(StickyButton):
    def __init__(self, parent_widget, str_btn_id, btns_parser, zoom_x, zoom_y):
        self.image = btns_parser.image(str_btn_id)
        super().__init__(parent_widget, str_btn_id, btns_parser, zoom_x, zoom_y)
        # self.image_pressed = ""
        # self.image_releasing = ""
        # self.image_light_sticky = ""
        # self.image_sticky = ""

    def _check_image_path(self, str_btn_status, image_replace=None):
        # 由于函数只在self.image存在时被调用，故省略self.image合法性判断
        file_name, file_ext = os.path.splitext(self.image)
        file_name_status = file_name + "_%d" % Enum_Btn_Status[str_btn_status]
        image_status = file_name_status + file_ext
        if not os.path.exists(image_status):
            # raise FileExistsError
            LOG.error("Fail to find the button's %s-image, replaced by released-image" % str_btn_status)
            # 如果找不到状态图片，则调用该按钮的默认图片
            return image_replace if image_replace else self.image
        else:
            return image_status

    def prepare_showing(self):
        if not os.path.exists(self.image):
            raise FileExistsError
        # pressed-path
        self.image_pressed = self._check_image_path("pressed")
        # sticky-path
        if self.sticky:
            self.image_sticky = self._check_image_path("sticky")
            self.image_light_sticky = self._check_image_path("light_sticky", self.image_sticky)

    def update_showing(self):
        # 根据 self.downEx 更新背景图
        if self.downEx == Enum_Btn_Status["released"]:
            path_pic = self.image
        elif self.downEx == Enum_Btn_Status["pressed"]:
            path_pic = self.image_pressed
        elif self.downEx == Enum_Btn_Status["sticky"]:
            path_pic = self.image_sticky
        elif self.downEx == Enum_Btn_Status["light_sticky"]:
            path_pic = self.image_light_sticky
        else:
            raise Exception("Unknown button pressing status.")
        self.setObjectName("tmp")
        self.setStyleSheet("#tmp{border-image:url(%s)}" % path_pic)


class NonImgButton(StickyButton):
    def __init__(self, parent_widget, str_btn_id, btns_parser, zoom_x, zoom_y, str_qss):
        self.str_qss = str_qss
        super().__init__(parent_widget, str_btn_id, btns_parser, zoom_x, zoom_y)

    def prepare_showing(self):
        if self.sticky:  # 对于没有image的sticky按键，通过QSS样式表切换sticky状态
            self.setProperty("sticky_status", "false")

    def update_showing(self):  # ??
        # 根据 self.downEx 更新QSS样式表（最好是只更新该按键的）
        if self.downEx == Enum_Btn_Status["released"]:
            self.setProperty("sticky_status", "false")
        elif self.downEx == Enum_Btn_Status["pressed"]:
            self.setProperty("sticky_status", "false")
        elif self.downEx == Enum_Btn_Status["sticky"]:
            self.setProperty("sticky_status", "sticky")
        elif self.downEx == Enum_Btn_Status["light_sticky"]:
            self.setProperty("sticky_status", "light_sticky")
        else:
            raise Exception("Unknown button pressing status.")

        # 更新按键的QSS样式表
        if self.str_qss:
            self.setStyleSheet(self.str_qss)

class ButtonFactory:
    def __init__(self, btns_parser: DataBtnParser, zoom_x, zoom_y, str_qss):
        self.btns_parser = btns_parser
        self.zoom_x = zoom_x
        self.zoom_y = zoom_y
        self.str_qss = str_qss

    def _check_type(self, btn_id):
        """ return True if hasattr('image'), else False """
        image = self.btns_parser.image(btn_id)
        return True if image else False

    def get_btn(self, parent_widget, str_btn_id):
        """ return a StickyButton Object """
        if self._check_type(str_btn_id):
            # LOG.debug("ImageButton[%s]" % str_btn_id)
            return ImageButton(parent_widget, str_btn_id, self.btns_parser,
                                self.zoom_x, self.zoom_y)
        else:
            # LOG.debug("NonImgButton[%s]" % str_btn_id)
            return NonImgButton(parent_widget, str_btn_id, self.btns_parser,
                                self.zoom_x, self.zoom_y, self.str_qss)
